﻿using System;
using System.Reflection;
using System.Text;
using Autofac;
using Mapster;
using MapsterMapper;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using WoodAdmin.Common.Extention;
using WoodAdmin.Common.Helper;
using WoodAdmin.Models.ViewModels;

namespace WoodAdmin
{
    public class Startup
    {
        readonly string MyAllowSpecificOrigins = "Any";
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            //配置跨域
            services.AddCors(options =>
            {
                options.AddPolicy(MyAllowSpecificOrigins,
                    builder =>
                    {
                        builder.WithOrigins("http://localhost:8080")
                               .AllowAnyMethod()
                               .AllowAnyHeader()
                               .AllowCredentials();
                    });
            });

            //调用addsession方法
            services.AddSession(options => {
                //Session失效时间
                options.IdleTimeout = TimeSpan.FromMinutes(60);
                //允许跨站点 cookie 使用
                options.Cookie.SameSite = SameSiteMode.Unspecified;
                //SameAsRequest确保session在https和http下页面seesion id都不会改变
                //Alawys只能在https下使用session
                //options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
                //options.Cookie.IsEssential = true;
            });

            //防止页面切换后Session ID改变，Seesion失效
            services.Configure<CookiePolicyOptions>(options =>
            {
                options.CheckConsentNeeded = context => false;//默认为true，改为false
                options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
            });

            // 注册Swagger服务
            services.AddSwaggerGen(c =>
            {
                #region 生成Api文档
                c.SwaggerDoc("v1", new OpenApiInfo
                {
                    Title = "WoodAdmin",
                    Version = "v1",
                    Description = "API OF WoodAdmin",
                    Contact = new OpenApiContact
                    {
                        Name = "Shepard",
                        Email = "wang.xinpeng@foxmail.com"
                    }
                });
                #endregion

                #region 自动注释
                //// 使用反射获取xml文件。并构造出文件的路径
                //var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
                //var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
                //// 启用xml注释. 该方法第二个参数启用控制器的注释，默认为false.
                //c.IncludeXmlComments(xmlPath, true);
                #endregion

                #region 权限验证
                // Add security definitions
                c.AddSecurityDefinition("WoodAdmin", new OpenApiSecurityScheme()
                {
                    Description = "Please enter into field the word 'Bearer' followed by a space and the JWT value",
                    Name = "Authorization",
                    In = ParameterLocation.Header,
                    Type = SecuritySchemeType.ApiKey,
                });
                c.AddSecurityRequirement(new OpenApiSecurityRequirement
                {
                    { new OpenApiSecurityScheme
                    {
                        Reference = new OpenApiReference()
                        {
                            Id = "WoodAdmin",
                            Type = ReferenceType.SecurityScheme
                        }
                    }, Array.Empty<string>() }
                });
                #endregion
            });

            // 添加验证服务
            services.AddAuthentication(x =>
            {
                x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            }).AddJwtBearer(o =>
            {
                o.TokenValidationParameters = new TokenValidationParameters
                {
                    // 是否开启签名认证
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(JwTHelper.SecretKey)),
                    // 发行人验证，这里要和token类中Claim类型的发行人保持一致
                    ValidateIssuer = true,
                    ValidIssuer = "jwtIssuer2021",//发行人
                    // 接收人验证
                    ValidateAudience = true,
                    ValidAudience = "jwtAudience2021",//订阅人
                    ValidateLifetime = true,
                    ClockSkew = TimeSpan.Zero,
                };
            });

            //ModelState的全局统一处理
            services.Configure<ApiBehaviorOptions>(options =>
            {
                //禁用默认行为(这种情况需要在每个action里边进行ModelState.IsValid验证)
                //options.SuppressModelStateInvalidFilter = true;

                //全局统一处理，返回错误信息
                options.InvalidModelStateResponseFactory = (context) =>
                {
                    var error = context.ModelState.GetValidationSummary();
                    return new JsonResult(new ResultViewModel { Status = ResultCode.Error, Msg = error, Obj = null });
                };
            });

            //添加Mapster配置
            TypeAdapterConfig.GlobalSettings.Default.IgnoreNullValues(true).PreserveReference(true);
            services.AddSingleton<IMapper>(new Mapper());

            //自定义中间件
            services.AddHttpContextHelperAccessor();

            //允许同步读取IO流(否则读取body信息的方法会报错->Synchronous operations are disallowed. Call ReadAsync or set AllowSynchronousIO to true instead.)
            services.Configure<KestrelServerOptions>(x => x.AllowSynchronousIO = true)
                    .Configure<IISServerOptions>(x => x.AllowSynchronousIO = true);

            //MVC配置
            services.AddControllersWithViews()
                    .SetCompatibilityVersion(CompatibilityVersion.Version_3_0)
                    .AddNewtonsoftJson()
                    .AddDataAnnotationsLocalization();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();

                #region SwaggerUI
                app.UseSwagger();

                app.UseSwaggerUI(c =>
                {
                    c.SwaggerEndpoint("/swagger/v1/swagger.json", "WoodAdmin-Api");
                    c.RoutePrefix = string.Empty;
                });
                #endregion
            }
            else
            {
                app.UseExceptionHandler("/Home/ServerError");
            }

            //app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseCookiePolicy();
            app.UseRouting();
            app.UseCors(MyAllowSpecificOrigins);
            app.UseAuthentication();
            app.UseAuthorization();
            app.UseSession();

            #region 此处写自定义中间件和三方中间件
            app.UseHttpContextHelper();
            #endregion

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }

        /// <summary>
        /// 项目启动时运行
        /// </summary>
        /// <param name="builder"></param>
        public void ConfigureContainer(ContainerBuilder builder)
        {
            //批量注册Service,注入Controller和View
            Assembly service = Assembly.Load("WoodAdmin");
            builder.RegisterAssemblyTypes(service)
            .Where(t => t.FullName.EndsWith("Service") && !t.IsAbstract) //类名以service结尾，且类型不能是抽象的　
            .InstancePerLifetimeScope() //生命周期，，
            .PropertiesAutowired(); //属性注入
        }
    }
}
